﻿using Tesla.Core;
using Tesla.Graphics;
using Tesla.Math;
using Tesla.Scene;
using Tesla.Scene.Shape;

namespace TeslaSamples.Scene {
    [AppDescription("Spatial Controllers", PlatformCapabilities.XNA4 | PlatformCapabilities.Direct3D10, "ControllerImage", "ControllerDesc", "ControllerSource")]
    public class ControllerSample : BasicApp {
        protected override void LoadContent() {
            Window.Title = "Spatial Controller Sample";

            //Create a box with a simple color material
            Box box = new Box("MyBox", 20, 20, 20);
            box.Material = ContentManager.Load<Material>("LitBasicColor.tem");
            box.Material.SetParameter("MatDiffuse", Color.Crimson.ToVector3());
            //Set the custom controller to the box, this will get updated everytime the scene graph is updated.
            box.AddController(new RotateScaleController(.5f, new Vector2(.2f, 3), 45, Vector3.Up));
            RootNode.AddChild(box);
        }

        /// <summary>
        /// Custom controller, every time the Spatial it is attached to 
        /// is updated, this controller will scale and rotate the Spatial
        /// unless if it's disabled.
        /// </summary>
        private class RotateScaleController : ISpatialController {
            private bool _enabled;
            private float _scale;
            private float _scaleIncrement;
            private Vector2 _scaleLimits;
            private float _angle;
            private float _rotIncrement;
            private Vector3 _rotAxis;

            public bool IsEnabled {
                get {
                    return _enabled;
                }
                set {
                    _enabled = value;
                }
            }

            /// <summary>
            /// Creates a new instance of <see cref="RotateScaleController"/>.
            /// </summary>
            /// <param name="scaleIncrement">The scale increment per-second.</param>
            /// <param name="scaleLimits">The scale limits, X is min, Y is max.</param>
            /// <param name="rotIncrement">The angle (in degrees) to rotate per-second</param>
            /// <param name="rotAxis">The axis to rotate about</param>
            public RotateScaleController(float scaleIncrement, Vector2 scaleLimits, float rotIncrement, Vector3 rotAxis) {
                _enabled = true;
                _scale = 1.0f;
                _scaleIncrement = scaleIncrement;
                _scaleLimits = scaleLimits;
                _angle = 0;
                _rotIncrement = rotIncrement;
                _rotAxis = rotAxis;
            }

            //After controllers are updated, a Spatial will
            //then check if its transforms, bounding volumes, or
            //lights have changed (and then update them). So computing
            //the scale/rotation here and setting them to the caller
            //will trigger a transform update after we execute.
            public void Update(GameTime time, Spatial caller) {
                if(_enabled) {
                    //Incrememnt rotation angle (we interpolate this based on the
                    //time since the last update/frame in order to animate smoothly)
                    _angle += _rotIncrement * (float) time.ElapsedTimeInSeconds;
                    if(_angle > 360) {
                        _angle = 0;
                    }
                    Quaternion q = Quaternion.FromAngleAxis(MathHelper.ToRadians(_angle), _rotAxis);
                    caller.Rotation = q;

                    //Increment scale, with the same idea
                    _scale += _scaleIncrement * (float) time.ElapsedTimeInSeconds;

                    //Check limits
                    if(_scale > _scaleLimits.Y) {
                        _scaleIncrement *= -1;
                        _scale = _scaleLimits.Y;
                    } else if(_scale < _scaleLimits.X) {
                        _scaleIncrement *= -1;
                        _scale = _scaleLimits.X;
                    }

                    caller.SetScale(_scale);
                }
            }
        }
    }
}
